package com.alinks.modbus.slave.handler;

import com.alinks.modbus.core.frame.ASCIIFrame;
import com.alinks.modbus.core.frame.ModbusFrame;
import com.alinks.modbus.core.utils.LRC;
import com.alinks.modbus.device.storage.DeviceManager;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ASCIIMessageHandler extends SimpleChannelInboundHandler<ASCIIFrame.Request> {

    protected final DeviceManager deviceManager = DeviceManager.newInstance();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ASCIIFrame.Request request) throws Exception {
        ASCIIFrame.Response response = null;
        if (request.getExcept() != null) {
            response = ASCIIFrame.Response.builder()
                    .header(request.getHeader())
                    .except(request.getExcept())
                    .build();
            ctx.writeAndFlush(response);
            return;
        }
        byte func = request.getBody().getFunc();
        short readCount = 0;
        switch (func) {
            case 1:
            case 2: readCount = (short) (request.getBody().getQuantity() / 8 + (request.getBody().getQuantity() % 8 > 0 ? 1 : 0));
            case 3:
            case 4: {
                readCount = (short) (readCount > 0 ? readCount : request.getBody().getQuantity() * 2);
                byte[] data = deviceManager.read(request.getHeader().getHeaderBytes()[0], func, request.getBody().getAddress(), readCount);
//                byte[] data = new byte[readCount];
                byte[] writeBytes = new byte[readCount + 3];
                ASCIIFrame.ASCIIHeader header = (ASCIIFrame.ASCIIHeader)request.getHeader();
                writeBytes[0] = header.getUnitId();
                writeBytes[1] = func;
                writeBytes[2] = (byte)readCount;
                for (short i = 0; i < readCount; i++) {
//                    data[i] = 0x00;
                    writeBytes[i + 3] = data[i];
                }
                response = ASCIIFrame.Response.builder()
                        .header(request.getHeader())
                        .body(ModbusFrame.ResponseBody.builder()
                                .func(func)
                                .returnLength((byte) data.length)
                                .data(data)
                                .build()
                        )
                        .build();
                byte crc = (byte) LRC.calc(writeBytes);
                response.setLrc(crc);
                response.setPrefix(request.getPrefix());
                response.setSuffix1(request.getSuffix1());
                response.setSuffix2(request.getSuffix2());
                ctx.writeAndFlush(response);
            } break;
            case 5:
            case 6: {
                deviceManager.write(request.getHeader().getHeaderBytes()[0],func, request.getBody().getAddress(), request.getBody().getWriteData());
                byte[] writeBytes = new byte[6];
                ASCIIFrame.ASCIIHeader header = (ASCIIFrame.ASCIIHeader)request.getHeader();
                writeBytes[0] = header.getUnitId();
                writeBytes[1] = func;
                writeBytes[2] = (byte)(request.getBody().getAddress() >> 8 & 0xff);
                writeBytes[3] = (byte)(request.getBody().getAddress() & 0xff);
                for (int i = 0; i < request.getBody().getWriteData().length; i++) {
                    writeBytes[4 + i] = request.getBody().getWriteData()[i];
                }
                response = ASCIIFrame.Response.builder()
                        .header(request.getHeader())
                        .body(ModbusFrame.ResponseBody.builder()
                                .func(request.getBody().getFunc())
                                .inAddress(request.getBody().getAddress())
                                .inData(request.getBody().getWriteData())
                                .build()
                        )
                        .build();
                response.setPrefix(request.getPrefix());
                response.setSuffix1(request.getSuffix1());
                response.setSuffix2(request.getSuffix2());
                byte crc = (byte) LRC.calc(writeBytes);
                response.setLrc(crc);
                ctx.writeAndFlush(response);
            } break;
            case 8: {

            } break;
            case 11: break;
            case 12: break;
            case 15:
            case 16: {
                deviceManager.write(request.getHeader().getHeaderBytes()[0], func, request.getBody().getAddress(), request.getBody().getWriteData());
                byte[] writeBytes = new byte[6];
                ASCIIFrame.ASCIIHeader header = (ASCIIFrame.ASCIIHeader)request.getHeader();
                writeBytes[0] = header.getUnitId();
                writeBytes[1] = func;
                writeBytes[2] = (byte)(request.getBody().getAddress() >> 8 & 0xff);
                writeBytes[3] = (byte)(request.getBody().getAddress() & 0xff);
                writeBytes[4] = (byte)(request.getBody().getQuantity() >> 8 & 0xff);
                writeBytes[5] = (byte)(request.getBody().getQuantity() & 0xff);
                response = ASCIIFrame.Response.builder()
                        .header(request.getHeader())
                        .body(ModbusFrame.ResponseBody.builder()
                                .func(request.getBody().getFunc())
                                .inAddress(request.getBody().getAddress())
                                .registerCount(request.getBody().getQuantity())
                                .build()
                        )
                        .build();
                response.setPrefix(request.getPrefix());
                response.setSuffix1(request.getSuffix1());
                response.setSuffix2(request.getSuffix2());
                byte crc = (byte) LRC.calc(writeBytes);
                response.setLrc(crc);
                ctx.writeAndFlush(response);
            } break;
            case 17: break;
            case 22: break;
            case 23: break;
            default: break;
        }
    }


}
